﻿using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using System.Reflection;
using System.Linq;
using YunFeng.TypeCreator.Models;

namespace YunFeng.TypeCreator
{
    public class TypeCreator
    {
        // 缓存已创建的动态类型
        private static readonly Dictionary<string, Type> _typeCache = new Dictionary<string, Type>();
        private static readonly object _lock = new object();

        public object CreateExpectationObject(TypeExpectation input, out Type type)
        {
            type = CreateDynamicType(input);
            object obj = Activator.CreateInstance(type);
            foreach (var item in input.Properties)
            {
                type.GetProperty(item.PropertyName).SetValue(obj, item.PropertyValue);
            }

            return obj;
        }

        public object CreateExpectationObject(TypeExpectation input)
        {
            Type type = CreateDynamicType(input);
            object obj = Activator.CreateInstance(type);
            foreach (var item in input.Properties)
            {
                type.GetProperty(item.PropertyName).SetValue(obj, item.PropertyValue);
            }

            return obj;
        }

        private Type CreateDynamicType(TypeExpectation input)
        {
            lock (_lock) // 确保线程安全
            {
                // 检查缓存中是否已存在该类型
                if (_typeCache.TryGetValue(input.ClassName, out Type cachedType))
                {
                    return cachedType;
                }

                // 创建程序集和模块
                AssemblyName assemblyName = new AssemblyName("DynamicAssembly");
                AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
                ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");

                // 定义类型
                TypeBuilder typeBuilder = moduleBuilder.DefineType(input.ClassName, TypeAttributes.Public);

                // 遍历属性并动态创建
                foreach (var property in input.Properties)
                {
                    CreatePropertyWithAttributes(typeBuilder, property.PropertyName, property.PropertyType, property.Attributes);
                }

                // 创建类型
                Type createdType = typeBuilder.CreateTypeInfo();
                _typeCache[input.ClassName] = createdType; // 缓存类型
                return createdType;
            }
        }

        private void CreatePropertyWithAttributes(TypeBuilder typeBuilder, string propertyName, Type propertyType, List<AttributeOnProperty> attributeOnProperty)
        {
            // 定义字段
            FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

            // 定义属性
            PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);

            // 添加特性到属性
            foreach (var attribute in attributeOnProperty)
            {
                var attributeBuilder = new CustomAttributeBuilder(attribute.AttributeType.GetConstructor(attribute.AttributeConstructorType), attribute.AttributeConstructorTypeParam);
                propertyBuilder.SetCustomAttribute(attributeBuilder);
            }

            // 定义 Get 方法
            MethodBuilder getMethodBuilder = typeBuilder.DefineMethod(
                "get_" + propertyName,
                MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                propertyType,
                Type.EmptyTypes);

            ILGenerator getIl = getMethodBuilder.GetILGenerator();
            getIl.Emit(OpCodes.Ldarg_0);
            getIl.Emit(OpCodes.Ldfld, fieldBuilder);
            getIl.Emit(OpCodes.Ret);

            // 定义 Set 方法
            MethodBuilder setMethodBuilder = typeBuilder.DefineMethod(
                "set_" + propertyName,
                MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                null,
                new[] { propertyType });

            ILGenerator setIl = setMethodBuilder.GetILGenerator();
            setIl.Emit(OpCodes.Ldarg_0);
            setIl.Emit(OpCodes.Ldarg_1);
            setIl.Emit(OpCodes.Stfld, fieldBuilder);
            setIl.Emit(OpCodes.Ret);

            // 关联属性与方法
            propertyBuilder.SetGetMethod(getMethodBuilder);
            propertyBuilder.SetSetMethod(setMethodBuilder);
        }
    }

    public class TypeCreatorBuilder
    {
        private TypeCreator Creator = new TypeCreator();
        private TypeExpectation _typeExpectation = new TypeExpectation();
        private List<PropertyDescription> _propertyDescriptions = new List<PropertyDescription>();
        private int _propertyIndex = -1;

        public TypeCreatorBuilder SetClassName(string className)
        {
            _typeExpectation = new TypeExpectation
            {
                ClassName = className,
                Properties = new List<PropertyDescription>()
            };
            return this;
        }

        public TypeCreatorBuilder WithProperty(string propertyName, Type propertyType, object propertyValue)
        {
            _propertyDescriptions.Add(new PropertyDescription
            {
                PropertyName = propertyName,
                PropertyType = propertyType,
                PropertyValue = propertyValue,
                Attributes = new List<AttributeOnProperty>()
            });
            _propertyIndex++;
            return this;
        }

        public TypeCreatorBuilder HasAttribute(Type attributeType, Type[] attributeConstructorType, object[] attributeConstructorTypeParam)
        {
            var attribute = new AttributeOnProperty
            {
                AttributeType = attributeType,
                AttributeConstructorType = attributeConstructorType,
                AttributeConstructorTypeParam = attributeConstructorTypeParam
            };
            _propertyDescriptions.ElementAt(_propertyIndex).Attributes.Add(attribute);
            return this;
        }

        public object CreateOjbect()
        {
            if (_propertyDescriptions.Count == 0)
            {
                throw new Exception("请先设置属性");
            }
            _typeExpectation.Properties = _propertyDescriptions;
            return Creator.CreateExpectationObject(_typeExpectation);
        }
    }
}